#include "settings.h"

using namespace std;
using namespace cv;

Settings::Settings() : m_GoodInput(false) {
    // 配置默认参数
    m_SettingName = "";
    m_BoardSize.width = 9;
    m_BoardSize.height = 6;
    m_SquareSize = 50;
    m_PatternToUse = "CHESSBOARD";
    m_NrFrames = 25;
    m_AspectRatio = 1;
    m_CalibZeroTangentDist = true;
    m_CalibFixPrincipalPoint = true;
    m_WritePoints = true;
    m_WriteExtrinsics = true;
    m_WriteGrid = true;
    m_OutputFileName = "";
    m_FlipVertical = false;
    m_Delay = 100;
    m_Input = "";
    m_FixK1 = false;
    m_FixK2 = false;
    m_FixK3 = false;
    m_FixK4 = true;
    m_FixK5 = true;
}

void Settings::write(FileStorage& fs) const                        //Write serialization for this class
{
    fs  << "Settings" << "{"
              << "BoardSize_Width"  << m_BoardSize.width
              << "BoardSize_Height" << m_BoardSize.height
              << "Square_Size"         << m_SquareSize
              << "Calibrate_Pattern" << m_PatternToUse
              << "Calibrate_NrOfFrameToUse" << m_NrFrames
              << "Calibrate_FixAspectRatio" << m_AspectRatio
              << "Calibrate_AssumeZeroTangentialDistortion" << m_CalibZeroTangentDist
              << "Calibrate_FixPrincipalPointAtTheCenter" << m_CalibFixPrincipalPoint

              << "Write_DetectedFeaturePoints" << m_WritePoints
              << "Write_extrinsicParameters"   << m_WriteExtrinsics
              << "Write_gridPoints" << m_WriteGrid
              << "Write_outputFileName"  << m_OutputFileName

              << "Show_UndistortedImage" << m_ShowUndistorsed

              << "Input_FlipAroundHorizontalAxis" << m_FlipVertical
              << "Input_Delay" << m_Delay
              << "Input" << m_Input
       << "}";
}

void Settings::read(const FileNode& node)                          //Read serialization for this class
{
    node["BoardSize_Width" ] >> m_BoardSize.width;
    node["BoardSize_Height"] >> m_BoardSize.height;
    node["Calibrate_Pattern"] >> m_PatternToUse;
    node["Square_Size"]  >> m_SquareSize;
    node["Calibrate_NrOfFrameToUse"] >> m_NrFrames;
    node["Calibrate_FixAspectRatio"] >> m_AspectRatio;
    node["Write_DetectedFeaturePoints"] >> m_WritePoints;
    node["Write_extrinsicParameters"] >> m_WriteExtrinsics;
    node["Write_gridPoints"] >> m_WriteGrid;
    node["Write_outputFileName"] >> m_OutputFileName;
    node["Calibrate_AssumeZeroTangentialDistortion"] >> m_CalibZeroTangentDist;
    node["Calibrate_FixPrincipalPointAtTheCenter"] >> m_CalibFixPrincipalPoint;
    node["Calibrate_UseFisheyeModel"] >> m_UseFisheye;
    node["Input_FlipAroundHorizontalAxis"] >> m_FlipVertical;
    node["Show_UndistortedImage"] >> m_ShowUndistorsed;
    node["Input"] >> m_Input;
    node["Input_Delay"] >> m_Delay;
    node["Fix_K1"] >> m_FixK1;
    node["Fix_K2"] >> m_FixK2;
    node["Fix_K3"] >> m_FixK3;
    node["Fix_K4"] >> m_FixK4;
    node["Fix_K5"] >> m_FixK5;

    validate();
}



void Settings::validate()
{
    m_GoodInput = true;
    if (m_BoardSize.width <= 0 || m_BoardSize.height <= 0)
    {
        cerr << "Invalid Board size: " << m_BoardSize.width << " " << m_BoardSize.height << endl;
        m_GoodInput = false;
    }
    if (m_SquareSize <= 10e-6)
    {
        cerr << "Invalid square size " << m_SquareSize << endl;
        m_GoodInput = false;
    }
    if (m_NrFrames <= 0)
    {
        cerr << "Invalid number of frames " << m_NrFrames << endl;
        m_GoodInput = false;
    }

    if (m_Input.empty())      // Check for valid input
            m_InputType = INVALID;
    else
    {
        if (m_Input[0] >= '0' && m_Input[0] <= '9')
        {
            stringstream ss(m_Input);
            ss >> m_CameraID;
            m_InputType = CAMERA;
        }
        else
        {
            if (isListOfImages(m_Input) && readStringList(m_Input, m_ImageList))
            {
                m_InputType = IMAGE_LIST;
                m_NrFrames = (m_NrFrames <= (int)m_ImageList.size()) ? m_NrFrames : (int)m_ImageList.size();
            }
            else {
                m_InputType = VIDEO_FILE;
                m_ImageList.clear();
            }
        }
        if (m_InputType == CAMERA)
            m_InputCapture.open(m_CameraID);
        if (m_InputType == VIDEO_FILE)
            m_InputCapture.open(m_Input);
        if (m_InputType != IMAGE_LIST && !m_InputCapture.isOpened())
                m_InputType = INVALID;
    }
    if (m_InputType == INVALID)
    {
        cerr << " Input does not exist: " << m_Input;
        m_GoodInput = false;
    }

    m_Flag = 0;
    if(m_CalibFixPrincipalPoint) m_Flag |= CALIB_FIX_PRINCIPAL_POINT;
    if(m_CalibZeroTangentDist)   m_Flag |= CALIB_ZERO_TANGENT_DIST;
    if(m_AspectRatio)            m_Flag |= CALIB_FIX_ASPECT_RATIO;
    if(m_FixK1)                  m_Flag |= CALIB_FIX_K1;
    if(m_FixK2)                  m_Flag |= CALIB_FIX_K2;
    if(m_FixK3)                  m_Flag |= CALIB_FIX_K3;
    if(m_FixK4)                  m_Flag |= CALIB_FIX_K4;
    if(m_FixK5)                  m_Flag |= CALIB_FIX_K5;

    if (m_UseFisheye) {
        // the fisheye model has its own enum, so overwrite the flags
        m_Flag = fisheye::CALIB_FIX_SKEW | fisheye::CALIB_RECOMPUTE_EXTRINSIC;
        if(m_FixK1)                   m_Flag |= fisheye::CALIB_FIX_K1;
        if(m_FixK2)                   m_Flag |= fisheye::CALIB_FIX_K2;
        if(m_FixK3)                   m_Flag |= fisheye::CALIB_FIX_K3;
        if(m_FixK4)                   m_Flag |= fisheye::CALIB_FIX_K4;
        if (m_CalibFixPrincipalPoint) m_Flag |= fisheye::CALIB_FIX_PRINCIPAL_POINT;
    }

    m_CalibrationPattern = NOT_EXISTING;
    if (!m_PatternToUse.compare("CHESSBOARD")) m_CalibrationPattern = CHESSBOARD;
    if (!m_PatternToUse.compare("CIRCLES_GRID")) m_CalibrationPattern = CIRCLES_GRID;
    if (!m_PatternToUse.compare("ASYMMETRIC_CIRCLES_GRID")) m_CalibrationPattern = ASYMMETRIC_CIRCLES_GRID;
    if (m_CalibrationPattern == NOT_EXISTING)
    {
        cerr << " Camera calibration mode does not exist: " << m_PatternToUse << endl;
        m_GoodInput = false;
    }
    m_AtImageList = 0;

}

Mat Settings::nextImage()
{
    Mat result;
    if( m_InputCapture.isOpened() )
    {
        Mat view0;
        m_InputCapture >> view0;
        view0.copyTo(result);
    }
    else if( m_AtImageList < m_ImageList.size() )
        result = imread(m_ImageList[m_AtImageList++], IMREAD_COLOR);

    return result;
}

bool Settings::readStringList(const string& filename, vector<string>& l )
{
    l.clear();
    FileStorage fs(filename, FileStorage::READ);
    if( !fs.isOpened() )
        return false;
    FileNode n = fs.getFirstTopLevelNode();
    if( n.type() != FileNode::SEQ )
        return false;
    FileNodeIterator it = n.begin(), it_end = n.end();
    for( ; it != it_end; ++it )
        l.push_back((string)*it);
    return true;
}

bool Settings::isListOfImages(const string& filename)
{
    string s(filename);
    // Look for file extension
    if( s.find(".xml") == string::npos && s.find(".yaml") == string::npos && s.find(".yml") == string::npos )
        return false;
    else
        return true;
}

deque<Mat> Settings::getImgList() {
    deque<Mat> ret;
    for (const string& str : m_ImageList) {
        Mat img = imread(str);
        ret.push_back(img);
    }
    return ret;
}
